package cn.jofei.server.http.filter;

import cn.jofei.common.ano.Component;
import cn.jofei.common.ano.Order;
import cn.jofei.common.netty.handler.RelayHandler;
import cn.jofei.common.netty.message.Message;
import cn.jofei.common.netty.protocol.MessageCodec;
import cn.jofei.common.netty.protocol.ProtocolFrameDecoder;
import cn.jofei.netty.hadler.ClientMessageHandler;
import cn.jofei.netty.hadler.HttpServerHandler;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Promise;
import javafx.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Clock;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static cn.jofei.server.interf.ServerQueue.CLIENT_REG_INFO;

/**
 * 提示客户端发起新 socket
 * 应该放在过滤连的最后
 *
 * @author Jofei Bao
 * @version ngbao v1.0.2
 * @date 2022年07月08日 23:48
 */
@Order(index = Integer.MAX_VALUE)
@Component
public class NoticeClientFilter implements HttpRequestFilter {

    public static final Logger log = LoggerFactory.getLogger(NoticeClientFilter.class);

    @Override
    public boolean doFilter(HttpRequest request, ChannelHandlerContext ctx) throws Exception {
        String subdomain = getSubdomain(request);

        // 拿到客户端 通信通道
        Pair<Message, Promise<Channel>> promisePair = HttpRequestFilter.getChannelPromise(ctx, subdomain);
        if (Objects.isNull(promisePair)) {
            return true;
        }
        Promise<Channel> promise = promisePair.getValue();
        long start = Clock.systemUTC().millis();
        HttpRequest retainRequest = ReferenceCountUtil.retain(request);

        promise.addListener(e -> {
            if (e.await(20, TimeUnit.SECONDS)) {
                if (e.isSuccess()) {
                    log.debug("获取客户端通道成功，耗时：{}ms", Clock.systemUTC().millis() - start);
                    // 客户端的 数据通道
                    Channel cChannel = (Channel) e.get();

                    cChannel.pipeline().remove(LoggingHandler.class);
                    cChannel.pipeline().remove(ProtocolFrameDecoder.class);
                    cChannel.pipeline().remove(IdleStateHandler.class);
                    cChannel.pipeline().remove(MessageCodec.class);
                    cChannel.pipeline().remove(ClientMessageHandler.class);
                    // 拿到客户端远程通道，继续读取
                    ctx.channel().config().setAutoRead(true);
                    cChannel.config().setAutoRead(true);
                    if (HttpMethod.CONNECT.equals(request.method())) {
                        HttpResponse connectedResponse = new DefaultHttpResponse(request.protocolVersion(), new HttpResponseStatus(200, "Connection Established"));
                        ctx.writeAndFlush(connectedResponse);
                    } else {
                        cChannel.pipeline().addLast(new HttpClientCodec(10 * 1024, 512 * 1024, 12 * 1024 * 1024));

                        // 自动释放引用计数的坑 使用 retainRequest
                        cChannel.writeAndFlush(retainRequest);
                        ctx.pipeline().remove(LoggingHandler.class);
                        ctx.pipeline().remove(HttpServerHandler.class);
                        ctx.pipeline().addLast(new RelayHandler(cChannel));
                        cChannel.pipeline().addLast(new RelayHandler(ctx.channel()));
                    }

                    // 发起通道搭建完成的通知
                    ChannelHandlerContext context = CLIENT_REG_INFO.get(subdomain);
                    context.writeAndFlush(promisePair.getKey().established());
                } else {
                    log.warn("Promise<Channel>客户端获取失败", e.cause());
                    ctx.close();
                }
            } else {
                log.warn("Promise<Channel>客户端获取超时");
            }

        });
        return true;
    }


}
